#include "Expr.h"
#include <stdio.h>
#include <string.h>
#include "SelectStatement.h"

namespace hsql {

	Expr::Expr(ExprType type)
		: type(type),
		expr(nullptr),
		expr2(nullptr),
		exprList(nullptr),
		select(nullptr),
		name(nullptr),
		table(nullptr),
		alias(nullptr),
		fval(0),
		ival(0),
		ival2(0),
		datetimeField(kDatetimeNone),
		isBoolLiteral(false),
		opType(kOpNone),
		distinct(false) {};

	Expr::~Expr() {
		delete expr;
		delete expr2;
		delete select;
		delete[] name;
		free(table);
		free(alias);

		if (exprList != nullptr) {
			for (Expr* e : *exprList) {
				delete e;
			}
			delete exprList;
		}
	}

	Expr* Expr::make(ExprType type) {
		Expr* e = new Expr(type);
		return e;
	}

	Expr* Expr::makeOpUnary(OperatorType op, Expr* expr) {
		Expr* e = new Expr(kExprOperator);
		e->opType = op;
		e->expr = expr;
		e->expr2 = nullptr;
		return e;
	}

	Expr* Expr::makeOpBinary(Expr* expr1, OperatorType op, Expr* expr2) {
		Expr* e = new Expr(kExprOperator);
		e->opType = op;
		e->expr = expr1;
		e->expr2 = expr2;
		return e;
	}

	Expr* Expr::makeBetween(Expr* expr, Expr* left, Expr* right) {
		Expr* e = new Expr(kExprOperator);
		e->expr = expr;
		e->opType = kOpBetween;
		e->exprList = new std::vector<Expr*>();
		e->exprList->push_back(left);
		e->exprList->push_back(right);
		return e;
	}

	Expr* Expr::makeCaseList(Expr* caseListElement) {
		Expr* e = new Expr(kExprOperator);
		// Case list expressions are temporary and will be integrated into the case
		// expressions exprList - thus assign operator type kOpNone
		e->opType = kOpNone;
		e->exprList = new std::vector<Expr*>();
		e->exprList->push_back(caseListElement);
		return e;
	}

	Expr* Expr::makeCaseListElement(Expr* when, Expr* then) {
		Expr* e = new Expr(kExprOperator);
		e->opType = kOpCaseListElement;
		e->expr = when;
		e->expr2 = then;
		return e;
	}

	Expr* Expr::caseListAppend(Expr* caseList, Expr* caseListElement) {
		caseList->exprList->push_back(caseListElement);
		return caseList;
	}

	Expr* Expr::makeCase(Expr* expr, Expr* caseList, Expr* elseExpr) {
		Expr* e = new Expr(kExprOperator);
		e->opType = kOpCase;
		e->expr = expr;
		e->expr2 = elseExpr;
		e->exprList = caseList->exprList;
		caseList->exprList = nullptr;
		delete caseList;
		return e;
	}

	Expr* Expr::makeLiteral(int64_t val) {
		Expr* e = new Expr(kExprLiteralInt);
		e->ival = val;
		return e;
	}

	Expr* Expr::makeLiteral(double value) {
		Expr* e = new Expr(kExprLiteralFloat);
		e->fval = value;
		return e;
	}

	Expr* Expr::makeLiteral(char* string) {
		Expr* e = new Expr(kExprLiteralString);
		e->name = new char[strlen(string) + 1];
		strcpy(e->name, string);
		return e;
	}
	Expr* Expr::makeLiteral(const char* string) {
		Expr* e = new Expr(kExprLiteralString);
		e->name = new char[strlen(string) + 1];
		strcpy(e->name, string);
		return e;
	}

	Expr* Expr::makeLiteral(bool val) {
		Expr* e = new Expr(kExprLiteralInt);
		e->ival = (int)val;
		e->isBoolLiteral = true;
		return e;
	}

	Expr* Expr::makeNullLiteral() {
		Expr* e = new Expr(kExprLiteralNull);
		return e;
	}

	Expr* Expr::makeColumnRef(char* name) {
		Expr* e = new Expr(kExprColumnRef);
		e->name = name;
		return e;
	}

	Expr* Expr::makeColumnRef(char* table, char* name) {
		Expr* e = new Expr(kExprColumnRef);
		e->name = name;
		e->table = table;
		return e;
	}

	Expr* Expr::makeStar(void) {
		Expr* e = new Expr(kExprStar);
		return e;
	}

	Expr* Expr::makeStar(char* table) {
		Expr* e = new Expr(kExprStar);
		e->table = table;
		return e;
	}

	Expr* Expr::makeFunctionRef(char* func_name, std::vector<Expr*>* exprList,
		bool distinct) {
		Expr* e = new Expr(kExprFunctionRef);
		e->name = func_name;
		e->exprList = exprList;
		e->distinct = distinct;
		return e;
	}

	Expr* Expr::makeArray(std::vector<Expr*>* exprList) {
		Expr* e = new Expr(kExprArray);
		e->exprList = exprList;
		return e;
	}

	Expr* Expr::makeArrayIndex(Expr* expr, int64_t index) {
		Expr* e = new Expr(kExprArrayIndex);
		e->expr = expr;
		e->ival = index;
		return e;
	}

	Expr* Expr::makeParameter(int id) {
		Expr* e = new Expr(kExprParameter);
		e->ival = id;
		return e;
	}

	Expr* Expr::makeSelect(SelectStatement* select) {
		Expr* e = new Expr(kExprSelect);
		e->select = select;
		return e;
	}

	Expr* Expr::makeExists(SelectStatement* select) {
		Expr* e = new Expr(kExprOperator);
		e->opType = kOpExists;
		e->select = select;
		return e;
	}

	Expr* Expr::makeInOperator(Expr* expr, std::vector<Expr*>* exprList) {
		Expr* e = new Expr(kExprOperator);
		e->opType = kOpIn;
		e->expr = expr;
		e->exprList = exprList;

		return e;
	}

	Expr* Expr::makeInOperator(Expr* expr, SelectStatement* select) {
		Expr* e = new Expr(kExprOperator);
		e->opType = kOpIn;
		e->expr = expr;
		e->select = select;

		return e;
	}

	Expr* Expr::makeExtract(DatetimeField datetimeField, Expr* expr) {
		Expr* e = new Expr(kExprFunctionRef);
		e->name = _strdup("EXTRACT");
		e->datetimeField = datetimeField;
		e->expr = expr;
		return e;
	}

	bool Expr::isType(ExprType exprType) const { return exprType == type; }

	bool Expr::isLiteral() const {
		return isType(kExprLiteralInt) || isType(kExprLiteralFloat) ||
			isType(kExprLiteralString) || isType(kExprParameter) ||
			isType(kExprLiteralNull);
	}

	bool Expr::hasAlias() const { return alias != nullptr; }

	bool Expr::hasTable() const { return table != nullptr; }

	const char* Expr::getName() const {
		if (alias != nullptr)
			return alias;
		else
			return name;
	}

	char* substr(const char* source, int from, int to) {
		int len = to - from;
		char* copy = (char*)malloc(len + 1);
		;
		strncpy(copy, source + from, len);
		copy[len] = '\0';
		return copy;
	}
	bool Expr::compare(Expr * val1, Expr * val2, OperatorType op)
	{
		if (val1->type != val2->type) {
			if ((val1->type == kExprLiteralInt || val1->type == kExprLiteralFloat) && (val2->type == kExprLiteralInt || val2->type == kExprLiteralFloat)) {
				double ex1= val1->type == kExprLiteralInt ? val1->ival : val1->fval;
				double ex2= val2->type == kExprLiteralInt ? val2->ival : val2->fval;
				if (op == kOpEquals) {
					return fabs(ex1 - ex2) < 1e-3;
				}
				else if (op == kOpGreater) {
					return ex1 > ex2;
				}
				else if (op == kOpGreaterEq) {
					return ex1 >= ex2;
				}
				else if (op == kOpLess) {
					return ex1 < ex2;
				}
				else if (op == kOpLessEq) {
					return ex1 <= ex2;
				}
			}
			else {
				return false;
			}
		}
		else {
			if (val1->type == kExprLiteralInt) {
				if (op == kOpEquals) {
					return val1->ival == val2->ival;
				}
				else if (op == kOpGreater) {
					return val1->ival > val2->ival;
				}
				else if (op == kOpGreaterEq) {
					return val1->ival >= val2->ival;
				}
				else if (op == kOpLess) {
					return val1->ival < val2->ival;
				}
				else if (op == kOpLessEq) {
					return val1->ival <= val2->ival;
				}
			}
			else if (val1->type == kExprLiteralFloat) {
				if (op == kOpEquals) {
					return val1->fval == val2->fval;
				}
				else if (op == kOpGreater) {
					return val1->fval > val2->fval;
				}
				else if (op == kOpGreaterEq) {
					return val1->fval >= val2->fval;
				}
				else if (op == kOpLess) {
					return val1->fval < val2->fval;
				}
				else if (op == kOpLessEq) {
					return val1->fval <= val2->fval;
				}
			}
			else if (val1->type == kExprLiteralNull) {
				if (op == kOpEquals || op == kOpGreaterEq || op == kOpLessEq) {
					return true;
				}
				else
					return false;
			}
			else if (val1->type == kExprLiteralString) {
				if (op == kOpEquals)
					return strcmp(val1->name,val2->name)==0;
				else
					return false;
			}
		}
		return false;
	}
}  // namespace hsql
